package com.bosssoft.controller;

import com.bosssoft.entity.po.FibVoucherDetailsPO;
import com.bosssoft.entity.vo.FibVoucherDetailsVO;
import com.bosssoft.entity.vo.PayRefundNoticeDirectlyVO;
import com.bosssoft.exception.DaoException;
import com.bosssoft.service.VoucherFeignService;
import com.bosssoft.response.Result;
import com.bosssoft.service.FibAccountService;
import com.codingapi.txlcn.tc.annotation.DTXPropagation;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.concurrent.*;

/**
 * 退款controller，实现业务流程
 * 1、申请凭证 -> 2、凭证盖章 -> 3、转账实操 -> 4、发送回单 -> 5、保存回单(盖章?)
 * @author 项昊天
 * @create 2021/8/20 15:35
 */
@RestController
@Slf4j
@RequestMapping("/refund")
public class RefundController {

  private static final Integer payRefundNoticeDirectlyCode = 22031;

  private static final String OPERATE = "refund";

  private static final String HAVE_OPERATED = "haveDone";

  @Autowired
  VoucherFeignService voucherFeignService;

  @Resource
  FibAccountService accountService;

  private static ExecutorService threadPool =
          new ThreadPoolExecutor(1,
                  5,
                  1000,
                  TimeUnit.MILLISECONDS,
                  new SynchronousQueue<Runnable>(),
                  Executors.defaultThreadFactory(),
                  new ThreadPoolExecutor.AbortPolicy());

  @Autowired
  RedisTemplate redisTemplate;

  // 每一次修改，都把数据放到redis里面去。然后从redis里面拿具体执行到哪一步。
  // 只开启一个线程，然后在该线程里面顺序执行所有操作，但是因为是耗时操作。所以需要前端一直请求后台的值。
  // 修改vo的字段。 预算id是必须要的。
  // 新建一个内部类？？？ 。。 调用xx方法的时候  执行该线程。。

  @PostMapping("/exec")
  public Result startRefund(@RequestBody @Validated PayRefundNoticeDirectlyVO vo) {
    redisTemplate.opsForValue().set(OPERATE + vo.getBgtId(), "status_0");
    exec(vo);
    return Result.ok().message("已提交银行处理。。。");
  }

  @PostMapping("/getStatus")
  public Result getStatus(@RequestParam("bgtId") Long bgtId) {

    Object status = redisTemplate.opsForValue().get(OPERATE + bgtId);
    if (status == null) {
      Result voucherResult = voucherFeignService.selectByBgtIdAndCode(payRefundNoticeDirectlyCode, bgtId);
      FibVoucherDetailsPO voucherDetails = (FibVoucherDetailsPO)voucherResult.getData().get("data");
      if (voucherDetails != null) {
        return Result.ok().message("该拨款项目已经生成过退款凭证了!已经操作过了!");
      }
      return Result.ok().message("请稍等, 正在生成凭证");
    }
    if ("error".equals(status)) {
      redisTemplate.delete(OPERATE + bgtId);
      return Result.ok().message("退款失败，请稍后重试!");
    }
    return Result.ok().message("");
  }

  /**
   * 异步运行
   * @param vo
   */
  @Async
  public void exec(PayRefundNoticeDirectlyVO vo) {
    try {
      Result voucherResult = refundVoucher(vo);
      if (voucherResult.getCode() != 200) {
        redisTemplate.opsForValue().set(OPERATE + vo.getBgtId(), "error");
        return;
      }
    } catch (Exception e) {
      log.info(e.getMessage());
    }
    try {
      Result transferResult = refundTransfer(vo);
      if (transferResult.getCode() != 200) {
        redisTemplate.opsForValue().set(OPERATE + vo.getBgtId(), "error");
        return;
      }
    } catch (Exception e) {
      log.info("退款转账操作出错啦!" + e.getMessage());
    }
    try {
      //TODO 盖章没写
    } catch (Exception e) {
      log.info("盖章过程出错啦!" + e.getMessage());
    }
  }

  /**
   * 退款第一步，生成退款凭证
   * 指定传播行为
   * @param vo
   * @return
   */
  @PostMapping("/voucher")
  @LcnTransaction
  public Result refundVoucher(@RequestBody @Validated PayRefundNoticeDirectlyVO vo) {

    Long bgtId = vo.getBgtId();
    //TODO 金额可能需要远程调用budget的接口查询,会更加合理。
    Result voucherResult = voucherFeignService.selectByBgtIdAndCode(payRefundNoticeDirectlyCode, bgtId);
    FibVoucherDetailsPO voucherDetails = (FibVoucherDetailsPO)voucherResult.getData().get("data");
    if (voucherDetails != null) {
      return Result.error().message("该拨款项目已经生成过退款凭证了!已经操作过了!");
    }

    try {
      Result result = voucherFeignService.addPayRefundNoticeDirectly(vo);
      log.info("测试远程调用@RestController的方法是否会被返回:result:{}", result);
      if (Integer.valueOf(200).equals(result.getCode())) {
        log.info("生成凭证成功");
        redisTemplate.opsForValue().set(OPERATE + bgtId, "status_1");
        return Result.ok().message("等待银行处理。。。").data("status", "status_1");
      }
    } catch (Exception e) {
      log.info("退款流程第一步生成凭证出错了!");
      redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
      throw DaoException.error();
    }
    redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
    return Result.error().message("远程调用生成凭证出问题了。。。");
  }

  /**
   * 转账操作。
   * 这里调用的是本地的service，有可能不需要LcnTransaction注解。
   * @param vo
   * @return
   */
  @PostMapping("/transfer")
  @LcnTransaction
  public Result refundTransfer(@RequestBody @Validated PayRefundNoticeDirectlyVO vo) {
    log.info("进入退款的转账环节");

    Long bgtId = vo.getBgtId();
    BigDecimal amount = new BigDecimal(vo.getMoneySmall());
    // 大于0
    if (vo.getMoneySmall() != null && amount.compareTo(new BigDecimal(0)) > 0) {
      log.info("amount:{}", vo.getMoneySmall());

      // 原本的收款人的账户变成了付款人账户
      String refundAccount = vo.getRefundAccountId();

      // 原本的付款人的账户变成了需要退款的账户
      String payeeAccount = vo.getPayeeAccountId();

      log.info("退款過程中付錢的refundAcc:{}, 退款過程中收錢的payeeAcc:{}", refundAccount, payeeAccount);
      Long payeeAccountInt = Long.parseLong(payeeAccount);
      Long refundAccountInt = Long.parseLong(refundAccount);
      log.info("退款過程中付錢的:refundAccInt:{}, 退款過程中收錢的:payeeAccInt:{}", refundAccountInt, payeeAccountInt);
      accountService.transferByAcc(refundAccountInt, payeeAccountInt, new BigDecimal(vo.getMoneySmall()));
      log.info("退款转账操作完成");

      redisTemplate.opsForValue().set(OPERATE + bgtId, "status_3");
      return Result.ok().message("退款成功!");
    }
    log.info("退款转账有问题");
    redisTemplate.opsForValue().set(OPERATE + bgtId, "error");
    return Result.error().message("退款失败!转账出问题了");
  }

//  因爲沒有回單。所以沒做回單。
//  @PostMapping("/voucher2")
//  public Result refundVoucher2() {
//
//    return Result.ok().message("回单生成成功!").data(null);
//  }

}